10.2. Lattice Graphics

Lattice is very capable and improves on traditional base graphics. The design goals were:

10.2.1. Lattice model

The main elements are:

Additional arguments to the high-level functions are used to activate common variants, and full flexibility is allowed through arbitrary user-defined functions (e.g. controlling the primary display through panel functions)

Each density plot is in an area called a panel. Panel sizes determined automatically. Axes annotated with tick marks and labels only along boundaries saving space. Strips atop each panel display info

A formula describes the content of the plots.

10.2.2. The formula argument

The first argument to many lattice functions is a formula argument describing variables to plot. A trellis formula looks like

     y ~ x | a * b

The ~ is what makes it a formula. The vertical bar denotes denotes conditioning and may be omitted.

So a and b are conditioning variables and the y and x are called the primary variables. At least one primary variable must be specified. (Unlike modelling functions, * and + in conditioning variables are treated identically in lattice. Thus, these are valid trellis functions in lattice.)

The formula can include method calls like sqrt() or log() to transform individual variables.

~ x
log(z) ~ x * y | a * b * c

Some examples.

Formula Lattice Plot
z ~ x * y Three dimensional plot
y1 + y2 ~ x Plot two \(y\)’s versus \(x\)
y ~ x1 + x2 Plot \(y\) versus two \(x\)s
y ~ x| g1 + g2 + ... Plot \(y\) versus \(x\) conditioned on \(g\)s
y ~ x| g1 * g2 * ... Plot \(y\) versus \(x\) conditioned on \(g\)s

Mostly, the \(g\)’s are factors or shingles.

Shingles are a generalization of factors for continuous variables. They are a way of discretizing continuous values (using cut, for example), except that the intervals may overlap. So an observation may be assigned to more than one interval.

The formula y ~ x | g indicates that several plots should be generated, showing \(y\) against \(x\) for each level of \(g\).

Each unique combination of the levels of the conditioning variables determines a packet, a subset of the primary variables for that combination.

10.2.3. Lattice Equivalents of Base Plots

Lattice Func. Lattice Description Base Analog
barchart Barcharts barplot
bwplot Boxplots boxplot
densityplot Conditional kernel density plots
dotplot Dotplots dotchart
histogram Histograms hist
qqmath Quantile–quantile plots qqnorm
stripplot Strip plots stripchart
qq Quantile–quantile plots qqplot
xyplot Scatterplots plot
levelplot Level plots image
contourplot Contour plots contour
cloud 3-dimensional scatterplot
wireframe 3-dimensional surfaces persp
splom Scatterplot matrices pairs
parallel Parallel coordinate plots

Various types of plots can be obtained by means of plot type specifications (argument type).

Type Effect Panel function
"p" Plot points
"l" Join points by line
"b" Both lines and points
"o" Points and lines overlaid
"S" "s" Plot as step function
"h" Drop lines to origin
"a" Join lines after averaging panel.average
"r" Join regression line panel.lmline
"smooth" Plot LOESS smooth panel.loess
"g" Plot a reference grid panel.grid

10.2.4. Annotation

Extra info can be added to a plot by specifying a panel function via the panel arguments.

For example, the default panel function for an xyplot is panel.xyplot().

This draws the default contents for a panel, so you want to call this as part of a custom panel function"

xyplot(mpg ~ wt, data = mtcars, panel=function(...)
       panel.xyplot(pch = 10, ...))

The arguments differ from function to function (see docs). Common ones: x, y and z and subscripts (indices to subset for each panel).

Function hooks, such as a prepanel function hook to control the size and scaling of panels (maybe make space for some text you want to show) and a strip function for controlling what gets drawn in the strips of a lattice plot.

10.2.5. A Quick Tour.

library(lattice)
data(iris)

Creating a separate panel for each plot

histogram(~Sepal.Length + Sepal.Width +
          Petal.Length + Petal.Width | Species,
          data=iris)

Plot all three species in a single panel.

densityplot(~Sepal.Length + Sepal.Width +
            Petal.Length + Petal.Width, group=Species,
            data=iris)

Plot without the points.

densityplot(~Sepal.Length + Sepal.Width +
            Petal.Length + Petal.Width, group=Species,
            data=iris, plot.points=FALSE, ref=TRUE)

Add in a legend

densityplot(~Sepal.Length + Sepal.Width + Petal.Length +
            Petal.Width, group=Species, data=iris,
            ref=TRUE, plot.points=FALSE,
            auto.key=list(columns=3))

Some formula details: * and + are treated similarly only in the conditioning part of the formula!

densityplot(~Sepal.Length * Sepal.Width * Petal.Length *
            Petal.Width, group=Species, data=iris,
            ref=TRUE, plot.points=FALSE,
            auto.key=list(columns=3))

An example normal quantile plot:

qqdata <- data.frame(x = c(rnorm(100), qnorm(ppoints(100))),
                     y = c(rep("x", 100),
                     rep("Normal Quantile", 100)))
qq(y ~ x, data=qqdata, col="red", lwd=3)

___

10.2.6. Famous Barley data

See for example ?barley. This is a data set containing total yield in bushels per acre for 10 varieties at 6 sites in each of two years.

str(barley)
## 'data.frame':    120 obs. of  4 variables:
##  $ yield  : num  27 48.9 27.4 39.9 33 ...
##  $ variety: Factor w/ 10 levels "Svansota","No. 462",..: 3 3 3 3 3 3 7 7 7 7 ...
##  $ year   : Factor w/ 2 levels "1932","1931": 2 2 2 2 2 2 2 2 2 2 ...
##  $ site   : Factor w/ 6 levels "Grand Rapids",..: 3 6 4 5 1 2 3 6 4 5 ...

We can examine some boxplots.

bwplot(yield ~ variety, data = barley)

bwplot(variety ~ yield, data = barley)

bwplot(variety ~ yield | year, data = barley)

Some dot plots.

dotplot(variety ~ yield, data = barley)

We add groups distinguished by color

dotplot(variety ~ yield, data = barley, groups = year,
        col=c("red", "blue"))

Condition on site.

dotplot(variety ~ yield | site, data = barley)

Group by year and then condition on site.

dotplot(variety ~ yield | site, data = barley, groups = year)

We need to add color to see difference.

dotplot(variety ~ yield | site, data = barley, groups = year,
        col=c("red", "blue"))

Note that conditioning on both makes things less obvious.

dotplot(variety ~ yield | site * year, data = barley)

So go back to condition on site and group by year and add a legend

dotplot(variety ~ yield | site, data = barley, groups = year,
        col=c("red", "blue"),
        auto.key=list(columns=2, col=c("red", "blue")))

The plot symbol color is screwed up in the previous dotplot. So we better have a separate symbol for each.

dotplot(variety ~ yield | site, data = barley, groups = year,
        pch=c(2, 4),
        col=c("red", "blue"),
        auto.key=list(columns=2, col=c("red", "blue")))

Hmmm. Plot symbol still screwed up, so build legend manually

dotplot(variety ~ yield | site, data = barley, groups = year,
        col=c("red", "blue"),
        pch=c(2, 4),
        key=list(text=list(c("1932", "1931")),
                 columns=2,
                 col=c("red", "blue")))

We can save plots to various formats such as pdf, png etc.

pdf(file="yield.pdf", paper="letter", width=8, height=10.5)
dotplot(variety ~ yield | site, data = barley, groups = year,
        col=c("red", "blue"),
        pch=c(2, 4),
        key=list(text=list(c("1932", "1931")),
                 columns=2,
                 col=c("red", "blue")))
dev.off()
## quartz_off_screen 
##                 2

10.2.7. Trellis Plots

One nice thing about lattice (and ggplot) is that the plots are actually objects. They don’t plot until you print them.

## Incremental updating
tp1 <- densityplot(~Sepal.Length+Sepal.Width+
  Petal.Length+Petal.Width,group=Species,data=iris)
print(tp1)

You can also update the object (will not affect tp1 above.)

tp2 <- update(tp1, plot.points=FALSE, ref=TRUE)
plot(tp2)

Add the legend.

tp3 <- update(tp2, auto.key=list(columns=3))
plot(tp3)

You can examine tp3 like any other object.

names(tp3)
##  [1] "formula"           "as.table"          "aspect.fill"      
##  [4] "legend"            "panel"             "page"             
##  [7] "layout"            "skip"              "strip"            
## [10] "strip.left"        "xscale.components" "yscale.components"
## [13] "axis"              "xlab"              "ylab"             
## [16] "xlab.default"      "ylab.default"      "xlab.top"         
## [19] "ylab.right"        "main"              "sub"              
## [22] "x.between"         "y.between"         "par.settings"     
## [25] "plot.args"         "lattice.options"   "par.strip.text"   
## [28] "index.cond"        "perm.cond"         "condlevels"       
## [31] "call"              "x.scales"          "y.scales"         
## [34] "panel.args.common" "panel.args"        "packet.sizes"     
## [37] "x.limits"          "y.limits"          "x.used.at"        
## [40] "y.used.at"         "x.num.limit"       "y.num.limit"      
## [43] "aspect.ratio"      "prepanel.default"  "prepanel"
tp3$legend
## $top
## $top$fun
## [1] "drawSimpleKey"
## 
## $top$args
## $top$args$text
## [1] "setosa"     "versicolor" "virginica" 
## 
## $top$args$columns
## [1] 3

And even pick off one of the plots.

tp3[1]

___

10.2.8. X-Y plots

tp = xyplot(lat~long, data=quakes, pch=".")
update(tp,
  main="Earthquakes in the Pacific Ocean\n(since 1964)")

We can apply a 3-level shingle, no overlap.

depthgroup <- equal.count(quakes$depth, number=3,
                          overlap=0)
magnitude <- equal.count(quakes$mag, number=2,
                         overlap=0)
xyplot(lat ~ long | depthgroup*magnitude,
       data=quakes,main="Fiji Earthquakes",
       ylab="latitude", xlab="longitude",
       pch=".",
       scales=list(x=list(alternating=c(1,1,1))),
       between=list(y=1),
       par.strip.text=list(cex=0.7),
       par.settings=list(axis.text=list(cex=0.7)))

10.2.9. Lattice Summary

Session Info

sessionInfo()
## R version 3.6.2 (2019-12-12)
## Platform: x86_64-apple-darwin19.2.0 (64-bit)
## Running under: macOS Catalina 10.15.2
## 
## Matrix products: default
## BLAS/LAPACK: /usr/local/Cellar/openblas/0.3.7/lib/libopenblasp-r0.3.7.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices datasets  utils     methods   base     
## 
## other attached packages:
## [1] lattice_0.20-38 rmarkdown_2.0   knitr_1.26      pkgdown_1.4.1  
## [5] devtools_2.2.1  usethis_1.5.1  
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.3        magrittr_1.5      MASS_7.3-51.5     pkgload_1.0.2    
##  [5] R6_2.4.1          rlang_0.4.3       fansi_0.4.1       stringr_1.4.0    
##  [9] tools_3.6.2       grid_3.6.2        pkgbuild_1.0.6    xfun_0.11        
## [13] sessioninfo_1.1.1 cli_2.0.1         withr_2.1.2       htmltools_0.4.0  
## [17] ellipsis_0.3.0    remotes_2.1.0     yaml_2.2.0        assertthat_0.2.1 
## [21] digest_0.6.23     rprojroot_1.3-2   crayon_1.3.4      processx_3.4.1   
## [25] callr_3.4.0       fs_1.3.1          ps_1.3.0          testthat_2.3.1   
## [29] evaluate_0.14     memoise_1.1.0     glue_1.3.1        stringi_1.4.5    
## [33] compiler_3.6.2    desc_1.2.0        backports_1.1.5   prettyunits_1.1.0